/*
board-games:	An educational framework for board games programming.
    Copyright (C) 2008  Daniel Gutson, FuDePAN
    
    This file is part of board-games.

    board-games is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    board-games is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
*/


/* this program expects at least two arguments:
    - the referee obj file
    - the player 1 obj file
    ...
    - the player n obj file
    
The referee is loaded in this process.
The players are loaded into separate processes. It's expected that:
    a) the referee was linked statically against the referee library
    b) the players were linked statically against the player library
    
*/

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/time.h>
#include "shobj.h"
//#include "msgqueue.h"
#include "pipe.h"
#include "players_side.h"
#include "referee_side.h"

//size_t MQueue::QUEUE_ID = 'a';

const int REFEREE_ARG = 1;
const int FIRST_PLAYER_ARG = 2;
const int MIN_NUMBER_OF_ARGS = 3;

inline int PLAYER2ARG(size_t player_id) { return player_id + FIRST_PLAYER_ARG; }
inline int ARG2PLAYER(size_t arg) { return arg - FIRST_PLAYER_ARG; }
inline size_t NUMBER_OF_PLAYERS(int argc)  { return size_t(argc - FIRST_PLAYER_ARG); }




static void show_usage();
static void show_banner(size_t players);
static void report_winner(size_t winner, size_t players);
static void initialize_randomizer();

static void referee_sig_alarm_handler(int sig);

int main(int argc, char* argv[])
{
    int ret;
    
    if (argc >= MIN_NUMBER_OF_ARGS)
    {
        signal(SIGALRM, referee_sig_alarm_handler);
        initialize_randomizer();

        try
        {
            show_banner(NUMBER_OF_PLAYERS(argc));
            PlayersProxyImpl players(NUMBER_OF_PLAYERS(argc));
            
                                            
            RefereeData referee;            
            referee.load(argv[REFEREE_ARG], &players);
            
            for(int i=FIRST_PLAYER_ARG; i < argc; i++)
                players.load(ARG2PLAYER(i), argv[i]);

            ret = referee.do_match();
            
            report_winner(ret, NUMBER_OF_PLAYERS(argc));
        }
        catch(const std::exception& e)
        {
            std::cerr << "[std] Error: " << e.what() << std::endl;
            ret = EXIT_FAILURE;
        }
        catch(const PlayerID playerID)
        {
            std::cerr << "Error in player " << playerID << std::endl;
            ret = EXIT_FAILURE; // TBD
        }
        catch(const char* err) // should not exist. All should be std::exception
        {
            std::cerr << "[err] Error: " << err << " errno: " << errno ;//<< std::endl;
            perror("");
            std::cerr << std::endl;
            ret = EXIT_FAILURE;
        }
        catch(...)
        {
            std::cerr << "[err] Error: Unknown" << " errno: " << errno ;//<< std::endl;
            perror("");
            std::cerr << std::endl;
            ret = EXIT_FAILURE;
        }
    }
    else
    {
        show_usage();
        ret = EXIT_FAILURE;
    }
    
    return ret;
}



static void referee_sig_alarm_handler(int sig)
{
    PlayerData::abort_current_connection();
}


static void show_usage()
{
    std::cout << "usage: main referee.so player_1.so [player_2.so ...]\n"; //TODO
}

static void show_banner(size_t players)
{
    std::cout << "Board programming " << MAJOR_VERSION << "." << MINOR_VERSION << "\n"
              << "Number of players: " << players << std::endl;
}

static void report_winner(size_t winner, size_t players)
{
    if (players > 1)
    {
        if (winner == 0) 
            std::cout << "\nDRAW.\n";
        else
            std::cout << "\nPlayer " << winner << " wins.\n";
    }
    else
    {
        if (winner == 0) 
            std::cout << "\nPlayer lost.\n";
        else
            std::cout << "\nPlayer wins.\n";
    }
}

static void initialize_randomizer()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    srand(tv.tv_usec);
}
